#ifdef N1MLT

#include "N1MLT.h"
#include "N1Lib.h"
#include "N1MLTDataStorage.h"
#include <new>
#include <list>

using namespace std;

static volatile N1UInt32      gMLT_ObjectsCount;

#ifdef WINDOWS

#include "N1DebugWin32.h"

#define MAX_STACK_COUNT 10

typedef struct 
{
    N0IntWithPointerSize stack[MAX_STACK_COUNT];
    void*                ptr;
    uint32               isStatic;
} MLT_OBJECT_INFO, *LPMLT_OBJECT_INFO;

#define ADD_ADDRESS(level) \
   if (cnt>level) { \
       if (continue_stack_trace) \
       { \
           frame = (unsigned int) __builtin_frame_address( level+1 ); \
           frame = ((frame & 0xFFFF0000) >> 16); \
           if (frame == base_frame) \
           { \
               stack[level] = (N0IntWithPointerSize)( __builtin_return_address( level+1 ) ); \
           } \
           else \
           { \
               stack[level] = 0; \
               continue_stack_trace = 0; \
           } \
       } else stack[level] = 0; \
   }
   

void N1MLT_GetStack(N0IntWithPointerSize *stack, uint32 cnt)
{
    unsigned int base_frame, frame;
    int continue_stack_trace;

    base_frame = (unsigned int) __builtin_frame_address(0);
    base_frame = ((base_frame & 0xFFFF0000) >> 16);

    continue_stack_trace = 1;

    ADD_ADDRESS(0)
    ADD_ADDRESS(1)
    ADD_ADDRESS(2)
    ADD_ADDRESS(3)
    ADD_ADDRESS(4)
    ADD_ADDRESS(5)
    ADD_ADDRESS(6)
    ADD_ADDRESS(7)
    ADD_ADDRESS(8)
    ADD_ADDRESS(9)
    ADD_ADDRESS(10)
    ADD_ADDRESS(11)
    ADD_ADDRESS(12)
    ADD_ADDRESS(13)
    ADD_ADDRESS(15)
    ADD_ADDRESS(16)
    ADD_ADDRESS(17)
    ADD_ADDRESS(18)
    ADD_ADDRESS(19)
    ADD_ADDRESS(20)
    ADD_ADDRESS(21)
    ADD_ADDRESS(22)
    ADD_ADDRESS(23)
    ADD_ADDRESS(24)
    ADD_ADDRESS(25)
    ADD_ADDRESS(26)
    ADD_ADDRESS(27)
    ADD_ADDRESS(28)
    ADD_ADDRESS(29)
}
#endif

extern void N1MltDataStorageInitialize(uint32 elementSize, uint32 maxElementCount);

void N1MLTInitialize()
{
#ifdef WINDOWS
    N1MltDataStorageInitialize(sizeof(MLT_OBJECT_INFO), 100000);
    gMLT_ObjectsCount = 0;
#endif
}

void N1MLT_OnAlloc(void* ptr)
{
#ifdef WINDOWS
    MLT_OBJECT_INFO moi;

    N1MLT_GetStack(moi.stack, sizeof(moi.stack)/sizeof(N0IntWithPointerSize));
    moi.ptr = ptr;
    moi.isStatic = 0;
    
    N1MltAddObject(ptr, &moi);
#endif
    gMLT_ObjectsCount++;
}

void N1MLT_OnFree(void* ptr)
{
#ifdef WINDOWS
    N1MltRemoveObject(ptr);
#endif
    gMLT_ObjectsCount--;
}

bool N1MLT_IsObjectInStaticPool(void* ptr)
{
    MLT_OBJECT_INFO *moi = (MLT_OBJECT_INFO*)N1MltFindObject(ptr);
    return moi->isStatic;
}

void N1MLT_AddToStaticPool(void* ptr)
{
    MLT_OBJECT_INFO *moi = (MLT_OBJECT_INFO*)N1MltFindObject(ptr);
    moi->isStatic = 1;
}


void N1MLT_PrintResults(FILE* file)
{
    fprintf(file, "\nUnreleased objects count: %d\n", gMLT_ObjectsCount);

    N1MltDataStorageIterator it;

    if(N1MltGetFirstObject(it))
    {
        do
        {
            LPMLT_OBJECT_INFO mltoi = (LPMLT_OBJECT_INFO)N1MltGetObject(it);

            if(N1MLT_IsObjectInStaticPool(mltoi->ptr)) continue;

            N1LogBacktrace(file, mltoi->stack, MAX_STACK_COUNT);

        } 
        while(N1MltGetNextObject(it));
    }
}

/*********************************************************/

void* operator new(size_t size) throw(bad_alloc)
{
    void * ptr = malloc(size);

    if(!ptr) 
    {
        throw bad_alloc();
    }
    N1MLT_OnAlloc(ptr);

    return ptr;
}

void operator delete(void * ptr) throw()
{
    if(!ptr) return;

    N1MLT_OnFree(ptr);

    free(ptr);
}

#endif
